package model

import (
	"errors"
	"fmt"
	"github.com/bgmp/config"
	log "github.com/sirupsen/logrus"

	"gorm.io/gorm"
	"strings"
	"time"
)

const (
	// 常量计数器 iota 下面的自动递增 1 类似枚举类
	BgNull = iota
	// 空腹
	EmptyStomach
	// 早餐后
	AfterBreakfast
	// 上午加餐
	MorningExtra
	// 午餐前
	BeforeLunch
	// 午餐后
	AfterLunch
	// 下午加餐
	AfternoonExtra
	// 晚餐前
	BeforeDinner
	// 晚餐后
	AfterDinner
	// 晚间加餐
	NightExtra
	// 睡前
	BeforeSleep
	// 夜间
	Night
	MalBgType = -1
)

var Tags = []string{
	"空腹",
	"早餐后",
	"上午加餐",
	"午餐前",
	"午餐后",
	"下午加餐",
	"晚餐前",
	"晚餐后",
	"晚间加餐",
	"睡前",
	"夜间",
}

type BgMeta struct {
	gorm.Model
	UserId uint `json:"user_id"`
}

// 继承了这两个结构体的元素
type Bg struct {
	BgMeta
	BgField
}

const (
	Bg_Table  = "bgs"
	Bg_Time   = "time"
	Bg_Value  = "value"
	Bg_Type   = "type"
	Bg_UserId = "user_id"
)

type BgField struct {
	Time  time.Time `json:"time"`
	Value float64   `json:"value"`
	Type  BgType    `json:"type"`
}

func (b *Bg) Create(db *gorm.DB) error {
	return db.Create(b).Error
}

func (b *Bg) Get(db *gorm.DB) error {
	return db.First(b).Error
}

func (b *Bg) Delete(db *gorm.DB) error {
	return db.Delete(b).Error
}

func (b *Bg) Save(db *gorm.DB) error {
	return db.Save(b).Error
}

func (b *Bg) Updates(db *gorm.DB) error {
	return db.Updates(b).Error
}

type BgView struct {
	ID    uint    `json:"id"`
	Type  string  `json:"type"`
	Time  int64   `json:"time"`
	Value float64 `json:"value"`
}

func FormatBgView(field *Bg) BgView {
	return BgView{
		ID:    field.ID,
		Type:  FormatBgType(field.Type),
		Time:  field.Time.Unix(),
		Value: field.Value,
	}
}

type BgSelectConditions struct {
	Start Time   `json:"start"`
	End   Time   `json:"end"`
	Type  BgType `json:"type"`
}

func ListBgs(db *gorm.DB, where map[string]any, conditions *BgSelectConditions) []Bg {
	var bgs []Bg
	sql := db.Table(Bg_Table).Where(where)
	// 条件拼接
	if conditions.Start.Time.Unix() != 0 {
		sql = sql.Where(GtEq(Bg_Time, conditions.Start.Time))
	}
	if conditions.End.Time.Unix() != 0 {
		sql = sql.Where(LtEq(Bg_Time, conditions.End.Time))
	}
	if conditions.Type != BgNull {
		sql = sql.Where(Eq(Bg_Type, conditions.Type))
	}
	// 执行 sql 语句
	sql.Find(&bgs)
	return bgs
}

func ListBgViews(db *gorm.DB, where map[string]any, conditions *BgSelectConditions) []BgView {
	var bgs []BgView
	rows := ListBgs(db, where, conditions)
	for i := range rows {
		// 筛除没有意义的数据
		if rows[i].Type == BgNull {
			continue
		}
		// 数据整形，调整好返回的格式
		bgs = append(bgs, FormatBgView(&rows[i]))
	}
	return bgs
}

func GetBgBefore(db *gorm.DB, userId uint, t Time, number int) []Bg {
	var bgs []Bg

	var b time.Time
	err := db.Table(Bg_Table).
		Select("MAX(time)").
		Where(LtEq(Bg_Time, t.Time)).
		Where(Eq(Bg_UserId, userId)).
		Scan(&b).Error
	if err != nil {
		log.Info("查询最大时间失败: %v", err)
		return bgs
	}
	if b.IsZero() {
		log.Println("未查询到任何小于 a 的记录，退出")
		return bgs
	}
	start := b.AddDate(0, 0, -1)
	end := b

	err = db.Table(Bg_Table).Where(LtEq(Bg_Time, end)).Where(Gt(Bg_Time, start)).Where(Eq(Bg_UserId, userId)).Order(Bg_Time + " desc").Find(&bgs).Error

	if err != nil {
		log.Fatalf("Between error: %v", err)
		return bgs
	}
	if len(bgs) >= number {
		return bgs
	}

	_ = db.Table(Bg_Table).Limit(number).Where(LtEq(Bg_Time, t.Time)).Where(Eq(Bg_UserId, userId)).Order(Bg_Time + " desc").Find(&bgs).Error

	return bgs
}

type BgType int

func (id *BgType) UnmarshalJSON(data []byte) error {
	raw := strings.Trim(string(data), "\"")
	*id = ParseBgType(raw)
	if *id == MalBgType {
		return errors.New("invalid bg type")
	}
	return nil
}

func ParseBgType(t string) BgType {
	switch t {
	case "":
		return BgNull
	case "空腹":
		return EmptyStomach
	case "早餐后":
		return AfterBreakfast
	case "上午加餐":
		return MorningExtra
	case "午餐前":
		return BeforeLunch
	case "午餐后":
		return AfterLunch
	case "下午加餐":
		return AfternoonExtra
	case "晚餐前":
		return BeforeDinner
	case "晚餐后":
		return AfterDinner
	case "晚间加餐":
		return NightExtra
	case "睡前":
		return BeforeSleep
	case "夜间":
		return Night
	}

	return MalBgType
}

func FormatBgType(t BgType) string {
	switch t {
	case EmptyStomach:
		return "空腹"
	case AfterBreakfast:
		return "早餐后"
	case MorningExtra:
		return "上午加餐"
	case BeforeLunch:
		return "午餐前"
	case AfterLunch:
		return "午餐后"
	case AfternoonExtra:
		return "下午加餐"
	case BeforeDinner:
		return "晚餐前"
	case AfterDinner:
		return "晚餐后"
	case NightExtra:
		return "晚间加餐"
	case BeforeSleep:
		return "睡前"
	case Night:
		return "夜间"
	}

	return ""
}

func SaveBgsFromFile(bf []*Bg) error {
	// 开启事务
	tx := config.DB.Begin()
	for _, bg := range bf {
		log.Info("保存数据", *bg)
		err := bg.Create(tx)
		if err != nil {
			tx.Rollback()
			log.Error(err)
			return err
		}
	}
	tx.Commit()
	return nil
}

type TimeForm struct {
	Year  uint `json:"year"`
	Month uint `json:"month"`
}

func GetFilters(db *gorm.DB, year uint, month uint, userId uint) ([]int, error) {
	var filters []int
	var sql string
	if year == 0 {
		sql = fmt.Sprintf(`select distinct year(time) from bgs where user_id = %d`, userId)
	} else if month == 0 {
		sql = fmt.Sprintf(`select distinct month(time) from bgs where user_id = %d and year(time) = %d`, userId, year)
	} else {
		sql = fmt.Sprintf(`select distinct day(time) from bgs where user_id = %d and year(time) = %d and month(time) = %d`, userId, year, month)
	}
	err := db.Raw(sql).Scan(&filters).Error
	if err != nil {
		return nil, err
	}
	return filters, nil
}
